<html>
	<head>
		<script src="js/Three.js"></script>
		<script src="js/custom/ThreeExtras.js"></script>
		<script src="js/Stats.js"></script>
		<title>OpenCloth WebGL Demo by Movania Muhammad Mobeen</title>
	</head>

	<body>
        <div align=right><p><b>OpenCloth WebGL Demo</b><br>
                            Integrator: Explicit Euler <br>
                             Step size: 1/60</p></div>
	</body>

	<script type="text/javascript">
		var camera, scene, renderer,
		    geometry, material, mesh, plane;
		var stats;
		var oldX, oldY, rX, rY, dist, state, D2R;
		var simulate = true;
		var EPSILON = 0.0001;
		var EPSILON2 = EPSILON*EPSILON;
		springs=new Array();
		F=new Array();
		V=new Array();
		var numX = 20;
		var numY = 20;
		var pickedPoint = -1;
		DEFAULT_DAMPING= 60   ;
		GRAVITY=new THREE.Vector3(0,-9.8,0);
		mass = 1;
		dt =  1.0/60;

		init();
		animate();

        function Spring () {
           	this.p1=-1;
           	this.p2=-1;
           	this.rest_length=0;
           	this.Ks=0.5;
           	this.Kd=0.5;
        }

		function AddSpring(a, b, ks, kd) {
			var spring=new Spring();
			spring.p1=a;
			spring.p2=b;
			spring.Ks=ks;
			spring.Kd=kd;
			var deltaP = new THREE.Vector3();
			deltaP.sub(mesh.geometry.vertices[a].position,mesh.geometry.vertices[b].position);
			spring.rest_length = Math.sqrt(deltaP.dot(deltaP));
			springs.push(spring);
		}

        function init() {
            var info = document.createElement('div');
            info.style.position = 'absolute';
            info.style.top = '10px';
            info.style.width = '100%';
            info.style.textAlign = 'center';
            document.body.appendChild(info);


		rX = 75;
		rY = 90;
		dist = 10;
		oldX = 0;
		oldY = 0;
		state = -1;
		D2R   = 0.0174532925199433;
		size = 4; //world space size of cloth
        hsize = size/2;
		u     = numX +1;
		v     = numY +1;

		KsStruct = 1000;
		KdStruct = 0.5;
		KsShear = 1000;
		KdShear = 0.5;
		KsBend = 1000;
		KdBend = 0.5;

       	camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );
		scene = new THREE.Scene();
		scene.add(camera);

		//draw axes
		var geoX = new THREE.Geometry();
		var geoY = new THREE.Geometry();
		var geoZ = new THREE.Geometry();

		geoX.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 0, 0)));
		geoX.vertices.push(new THREE.Vertex(new THREE.Vector3(1, 0, 0)));

		geoY.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 0, 0)));
		geoY.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 1, 0)));

		geoZ.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 0, 0)));
		geoZ.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 0, 1)));

		var lineX = new THREE.Line(geoX, new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 3 }));
		var lineY = new THREE.Line(geoY, new THREE.LineBasicMaterial({ color: 0x00ff00, linewidth: 3 }));
		var lineZ = new THREE.Line(geoZ, new THREE.LineBasicMaterial({ color: 0x0000ff, linewidth: 3 }));

		scene.add(lineX);
		scene.add(lineY);
		scene.add(lineZ);



        geometry = new THREE.PlaneGeometry( -20, 20, 10, 10);

		plane = new THREE.Mesh(  geometry,new THREE.MeshBasicMaterial( { color: 0x0000ff, wireframe:true } ));
		plane.doubleSided = true;
		plane.rotation.x = 90 * D2R;
		scene.add( plane );

       	geometry = new THREE.Geometry();

       	//Fill in vertices of cloth mesh
        for(var j=0;j<v;j++) {
			for(var i=0;i<u;i++) {
			    var tmp = new THREE.Vertex(new THREE.Vector3( ((i/(u-1)) *2.0-1.)* hsize,size+1, ((j/(v-1.0) )* size)));
				F.push(new THREE.Vector3(0,0,0));
				V.push(new THREE.Vector3(0,0,0));
			    geometry.vertices.push(tmp);
			}
	    }

	    //Now fill in face information
	    for (var i = 0; i < numY; i++) {
			for (var j = 0; j < numX; j++) {
				i0 = i * (numX+1) + j;
				i1 = i0 + 1;
				i2 = i0 + (numX+1);
				i3 = i2 + 1;
				/*
				if ((j+i)%2) {
				    var face1 = new THREE.Face3(i0, i2, i1);
				    var face2 = new THREE.Face3(i1, i2, i3);
				    geometry.faces.push(face1);
				    geometry.faces.push(face2);
				} else {
					var face1 = new THREE.Face3(i0, i2, i3);
					var face2 = new THREE.Face3(i0, i3, i1);
					geometry.faces.push(face1);
					geometry.faces.push(face2);
				}
				*/
				//using Face4 instead of Face3 based on suggestion of Mr.Doob
				var face = new THREE.Face4(i0, i2, i3, i1);
				geometry.faces.push(face);
			}
		}

        geometry.computeFaceNormals();
		geometry.computeCentroids();

		mesh = new THREE.Mesh( geometry,new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe:true } )); //new THREE.MeshNormalMaterial() );
		mesh.doubleSided = true;
		mesh.geometry.dynamic = true; //for WebGL renderer

		scene.add( mesh );

		//setup springs
		// Horizontal
		for (l1 = 0; l1 < v; l1++)	// v
			for (l2 = 0; l2 < (u - 1); l2++) {
				AddSpring((l1 * u) + l2,(l1 * u) + l2 + 1,KsStruct,KdStruct);
			}

		// Vertical
		for (l1 = 0; l1 < (u); l1++)
			for (l2 = 0; l2 < (v - 1); l2++) {
				AddSpring((l2 * u) + l1,((l2 + 1) * u) + l1,KsStruct,KdStruct);
			}

		// Shearing Springs
		for (l1 = 0; l1 < (v - 1); l1++)
			for (l2 = 0; l2 < (u - 1); l2++) {
				AddSpring((l1 * u) + l2,((l1 + 1) * u) + l2 + 1,KsShear,KdShear);
				AddSpring(((l1 + 1) * u) + l2,(l1 * u) + l2 + 1,KsShear,KdShear);
			}

		// Bend Springs
		for (l1 = 0; l1 < (v); l1++) {
			for (l2 = 0; l2 < (u - 2); l2++) {
				AddSpring((l1 * u) + l2,(l1 * u) + l2 + 2,KsBend,KdBend);
			}
			AddSpring((l1 * u) + (u - 3),(l1 * u) + (u - 1),KsBend,KdBend);
		}
		for (l1 = 0; l1 < (u); l1++) {
			for (l2 = 0; l2 < (v - 2); l2++) {
				AddSpring((l2 * u) + l1,((l2 + 2) * u) + l1,KsBend,KdBend);
			}
			AddSpring(((v - 3) * u) + l1,((v - 1) * u) + l1,KsBend,KdBend);
		}

		console.log("Total springs: "+springs.length);
		console.log("Total forces: "+F.length);
		console.log("Total velocities: "+V.length);

       //	renderer = new THREE.CanvasRenderer();
       	renderer = new THREE.WebGLRenderer();
       	renderer.setSize( window.innerWidth, window.innerHeight );

       	document.body.appendChild( renderer.domElement );

       	stats = new Stats();
       	stats.domElement.style.position = 'absolute';
       	stats.domElement.style.top = '0px';
       	document.body.appendChild(stats.domElement);

       	document.addEventListener( 'mousedown', onMouseDown, false );
       	document.addEventListener( 'mousemove', onMouseMove, false );
       	document.addEventListener( 'mouseup', onMouseUp, false );
   	}

	function onMouseDown( event ) {
		event.preventDefault();

		//0 left mouse button
		//1 middle mouse button
		//2 right mouse button
		state = event.button;

		oldX = event.clientX;
		oldY = event.clientY;

		if (state == 0) {

		}
	}

	function onMouseMove(event) {

		if(state == 1) {
			dist = dist * (1 + (event.clientY - oldY)/60.0);
		} else if(state == 0){

			rY += (event.clientX - oldX)/5.0;
			rX += (event.clientY - oldY)/5.0;
		}
		oldX = event.clientX;
		oldY = event.clientY;
		render();
	}

	function onMouseUp( event ) {
		event.preventDefault();
		//console.log("Rx: "+rX+",Ry: "+rY);

		state = -1;
	}

	function computeForces() {
	   for(var i=0;i<mesh.geometry.vertices.length;i++) {
	      F[i].x=0;
	      F[i].y=0;
	      F[i].z=0;
	      if(i!=0 && i!= numX)
	         F[i].addSelf(GRAVITY);

	      F[i].subSelf( V[i].multiplyScalar(DEFAULT_DAMPING));
	   }

	   //add spring forces
	   var deltaP = new THREE.Vector3();
	   var deltaV = new THREE.Vector3();
	   for(var i=0;i<springs.length;i++) {
	   		var p1 = mesh.geometry.vertices[springs[i].p1].position;
	   		var p2 = mesh.geometry.vertices[springs[i].p2].position;

	   		var v1 = V[springs[i].p1];
	   		var v2 = V[springs[i].p2];


	   		deltaP.sub(p1,p2);
	   		deltaV.sub(v1,v2);
	   		var dist = deltaP.length();
            //console.log("p1: "+p1+", p2: "+p2+" Dis: "+dist);

	   		var leftTerm  = -springs[i].Ks * (dist-springs[i].rest_length);
	   		var rightTerm = -springs[i].Kd * ((deltaV.dot(deltaP))/dist);
	   		var springForce = (deltaP.normalize()).multiplyScalar(leftTerm + rightTerm);

	   		if(springs[i].p1 != 0 && springs[i].p1 != numX)
	   			F[springs[i].p1].addSelf(springForce);
	   		if(springs[i].p2 != 0 && springs[i].p2 != numX )
	   			F[springs[i].p2].subSelf(springForce);
		}
	}

	function integrateEuler() {
		var deltaTimeMass = dt/ mass;
        var N = mesh.geometry.vertices.length;
        var oldP = mesh.geometry.vertices[N-1].position.clone();
        geometry.__dirtyVertices = true;
	 	for(var i=0;i<N;i++) {
			var oldV = V[i];

			V[i].addSelf(F[i].multiplyScalar(deltaTimeMass));
			mesh.geometry.vertices[i].position.addSelf(oldV.multiplyScalar(dt));

			if(mesh.geometry.vertices[i].position.y <0) {
				mesh.geometry.vertices[i].position.y = 0;
			}
		}

		var diff = new THREE.Vector3(0,0,0);
		diff.sub(oldP, mesh.geometry.vertices[N - 1].position);

		simulate = (diff.lengthSq()> EPSILON2);
        //
		//console.log("Diff: " + diff.length());
	}

	function doPhysics() {

  	   computeForces();
  	   //console.log("Force: "+F[1].z+" Vel: "+V[1].z);
  	   integrateEuler();

	}

    function animate() {

       requestAnimationFrame(animate);
       if (simulate)
           doPhysics();
       else {

           if (mesh.material.color.r == 1) {
               mesh.material.color.r = 0;
               mesh.material.color.g = 1;
               console.log("Cloth passive");
           }

       }
       render();
       stats.update();
    }

   	function render() {
	/*
		mesh.position.z = dist;
		mesh.rotation.x = rX*D2R;
		mesh.rotation.z = rY*D2R;

	    plane.position.z = dist;
	    plane.rotation.x = rX*D2R;
	    plane.rotation.z = rY*D2R;

	*/
	   	var theta = rY * D2R;
	   	var phi = rX * D2R;
	   	var radius = dist;
	   	camera.position.x = radius * Math.sin(phi) * Math.cos(theta);
	   	camera.position.y = radius * Math.cos(phi);
	   	camera.position.z = radius * Math.sin(phi) * Math.sin(theta);
	   	camera.lookAt(new THREE.Vector3(0, 0, 0));

		renderer.render( scene, camera );

	}
	</script>
</html>